/* vcc/vcc.c 
 * 
 * This file is part of vcc. 
 * 
 * vcc is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 * 
 * vcc is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with vcc. If not, see <https://www.gnu.org/licenses/>
 */ 




#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <signal.h>

#include <vcc/vcc.h>
#include <vcc/version.h>

#define clear_req(req) 	memset(req, 0, REQ_SIZE)


extern int fd;

int current_sid = 0;

int vcc_login_bh(struct vcc_request *);
int do_cmd_ls_bh(struct vcc_request *);
int do_cmd_lsse_bh(struct vcc_request *);


int init_socket(const char *server_addr, int server_port) {
	struct sockaddr_in addr, server;

	if (unlikely((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)) {
		perror("socket()");

		exit(1);
	}

	memset(&addr, 0, sizeof(struct sockaddr_in));

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(0);

	if (unlikely(bind(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) < 0)) {
		perror("bind()");

		exit(1);
	}

	memset(&server, 0, sizeof(struct sockaddr_in));

	server.sin_family = AF_INET;
	server.sin_port = htons(server_port);

	if (unlikely(!inet_aton(server_addr, &server.sin_addr))) {
		fprintf(stderr, "invalid ip address. "
				"leave blank if you want to connect the default server. \n");

		exit(1);
	}

	if (unlikely(connect(fd, (struct sockaddr *) &server, sizeof(struct sockaddr_in)) < 0)) {
		perror("connect()");

		exit(1);
	}

	return fd;
}



int send_msg(const char *msg, const char *usrname) {
	struct vcc_request req;

	clear_req(&req);

	req.magic = htonl(VCC_MAGIC);
	req.reqtype = htonl(REQ_MSG_SEND);
	req.uid = 0;
	req.session = htonl(current_sid);
       
	strncpy(req.usrname, usrname, USRNAME_SIZE);
	strncpy(req.msg, msg, MSG_SIZE);
	write(fd, &req, REQ_SIZE);

	return 0;
}


int get_msgs(void) {
	struct vcc_request req;

	clear_req(&req);

	req.magic = htonl(VCC_MAGIC);
	req.reqtype = htonl(REQ_MSG_NEW);
	req.uid = 0;

	write(fd, &req, REQ_SIZE);

	return 0;
}


int read_msg(struct vcc_request *buf) {
	return read(fd, buf, REQ_SIZE);
}


int get_users(void) {
	struct vcc_request req;

	clear_req(&req);

	req.magic = htonl(VCC_MAGIC);
	req.reqtype = htonl(REQ_CTL_USRS);
	req.uid = 0;

	write(fd, &req, REQ_SIZE);

	return 0;
}

int get_sessions(void) {
	struct vcc_request req;

	clear_req(&req);

	req.magic = htonl(VCC_MAGIC);
	req.reqtype = htonl(REQ_CTL_SESS);
	req.uid = 0;

	write(fd, &req, REQ_SIZE);

	return 0;
}


int do_bh(struct vcc_request *req) {
	switch (ntohl(req->reqtype)) {
	case REQ_CTL_LOGIN:
		vcc_login_bh(req);

		break;

	case REQ_CTL_USRS:
		do_cmd_ls_bh(req);

		break;

	case REQ_CTL_SESS:
		do_cmd_lsse_bh(req);

		break;

	default:
		fprintf(stderr, "*** unexpected packet type %d. \n", ntohl(req->reqtype));

		break;
	}

	return 0;
}


int vcc_login(char *usrname, char *passwd) {
	struct vcc_request req;

	clear_req(&req);

	req.magic = htonl(VCC_MAGIC);
	req.reqtype = htonl(REQ_CTL_LOGIN);

	strncpy(req.usrname, usrname, USRNAME_SIZE);
	strncpy(req.msg, passwd, MSG_SIZE);

	write(fd, &req, REQ_SIZE);

	return 0;
}


int vcc_login_bh(struct vcc_request *req) {
	if (ntohl(req->reqtype) != REQ_CTL_LOGIN) {
		fprintf(stderr, "*** bad package. \n");

		return 1;
	}

	if (ntohl(req->uid)) 
		printf("ready. \n");

	else {
		fprintf(stderr, "login failed: wrong password or user doesn't exists. \n");
		fprintf(stderr, "if you haven't got an account on this server, ask the administrator " 
				"and you can get one. \n");

		exit(1);
	}

	return 0;
}


int vcc_create_session(char *name) {
	struct vcc_request req;

	clear_req(&req);

	req.magic = htonl(VCC_MAGIC);
	req.reqtype = htonl(REQ_CTL_NEWSE);

	strncpy(req.usrname, name, USRNAME_SIZE);

	write(fd, &req, REQ_SIZE);

	return 0;
}


int join_session(int sid) {
	struct vcc_request req;

	clear_req(&req);

	req.magic = htonl(VCC_MAGIC);
	req.reqtype = htonl(REQ_CTL_JOINS);
	req.session = htonl(sid);

	write(fd, &req, REQ_SIZE);

	return 0;

}


